package com.wmb.userservice.service.Impl;

import com.wmb.userservice.mapper.UserMapper;
import com.wmb.userservice.model.dto.LoginUserDTO;
import com.wmb.userservice.model.dto.UserPageDTO;
import com.wmb.userservice.model.pojo.User;
import com.wmb.userservice.model.vo.LoginUserVO;
import com.wmb.userservice.openFeigin.UserFeignClient;
import com.wmb.userservice.properties.JwtProperties;
import com.wmb.userservice.service.UserService;
import com.wmb.userservice.utils.BaseContextUtil;
import com.wmb.userservice.utils.JwtUtil;
import com.wmb.userservice.utils.PageResult;
import com.wmb.userservice.utils.Result;
import io.seata.spring.annotation.GlobalTransactional;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.DigestUtils;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;


@Service
public class UserServiceImpl implements UserService {
    // 手机号正则
    private static final String PHONE_REGEX = "^1[3-9]\\d{9}$";
    // 邮箱正则
    private static final String EMAIL_REGEX = "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,6}$";
    // 用户ID正则
    private static final String USER_NAME_REGEX = "^[a-zA-Z0-9]{1,18}$";
    // 密码正则
    private static final String PASSWORD_REGEX = "^(?=.*[0-9])(?=.*[a-zA-Z]).{6,20}$";

    // 默认密码
    private static final String DEFAULT_PASSWORD = "123456";

    @Autowired
    private  UserMapper userMapper;

    @Autowired
    private JwtProperties jwtProperties;

    @Autowired
    private UserFeignClient userFeignClient;

    /**
     * 根据用户ID查询用户信息
     * @param userId
     * @return
     */
    @GlobalTransactional(rollbackFor = Exception.class)
    @Override
    public Result<User> getUserById(Long userId) {
        // 获取本地线程中登录用户的 userid
        Long localUserId = BaseContextUtil.getCurrentId();

        // 对比当前登录用户的 localUserId 权限和需要查询的 userId 的权限
        // 调用权限服务
        if (!userFeignClient.permissionCheckout(localUserId, userId)) {
            return Result.error("权限不足");
        }
        // 查询用户信息
        User user = userMapper.getUserByUserId(userId);
        user.setPassword(null);
        return Result.success(user);
    }

    /**
     * 登录
     * ID、手机号、邮箱   三种登录情况
     *
     * @param loginUserDTO
     * @return
     */
    @GlobalTransactional(rollbackFor = Exception.class)
    @Override
    public Result<LoginUserVO> login(LoginUserDTO loginUserDTO) {

        // 获取用户名密码
        String loginUsername = loginUserDTO.getUsername();
        String loginPassword = loginUserDTO.getPassword();

        // 先验证密码是否符合规范
        // 正则表达式转换为对象
        Pattern pattern = Pattern.compile(PASSWORD_REGEX);
        boolean passwordMatches = pattern.matcher(loginPassword).matches();
        if(!passwordMatches)
            return Result.error("密码格式错误");
        // 获取加密后的密码
        String md5Password = DigestUtils.md5DigestAsHex(loginPassword.getBytes());

        // 三种登录方式分别讨论
        //  用户名登录

        // 判断是否符合正则
        if (patternREGEX(USER_NAME_REGEX, loginUsername)) {
            // 查找数据库中是否有该用户
            User user = userMapper.getUserByUsername(loginUsername);
            // 如果存在该用户且密码正确，则返回用户信息
            if (user != null && user.getPassword().equals(md5Password)) {
                user.setPassword(null);
                // 调用方法生成Jwt，并返回用户信息
                return buildJwt(user);
            }
        }

        // 手机号登录
        // 判断是否符合正则
        if (patternREGEX(PHONE_REGEX,loginUsername)) {
            // 查找数据库中是否有该用户
            User user = userMapper.getUserByUsername(loginUsername);
            // 如果存在该用户且密码正确，则返回用户信息
            if (user != null && user.getPassword().equals(md5Password)) {
                user.setPassword(null);
                // 调用方法生成Jwt，并返回用户信息
                return buildJwt(user);
            }
        }

        // 邮箱登录
        // 判断是否符合正则
        if (patternREGEX(EMAIL_REGEX,loginUsername)) {
            // 查找数据库中是否有该用户
            User user = userMapper.getUserByUsername(loginUsername);
            // 如果存在该用户且密码正确，则返回用户信息
            if (user != null && user.getPassword().equals(md5Password)) {
                user.setPassword(null);
                // 调用方法生成Jwt，并返回用户信息
                return buildJwt(user);
            }
        }
        return Result.error("用户名或密码错误");
    }

    /**
     * 注册
     * @param user
     * @return
     */
    @Override
    @GlobalTransactional(rollbackFor = Exception.class)
    public Result<User> register(User user) {

        // 判断是否传入了userID
        if (user.getUserId() != null){
            return Result.error("userId 应为空");
        }
        // 验证是否为空
        if ((user.getUsername() == null || user.getUsername().equals("")) ||
                (user.getPassword() == null || user.getPassword().equals("")) ||
                (user.getEmail() == null || user.getEmail().equals("")) ||
                (user.getPhone() == null || user.getPhone().equals(""))){
            return Result.error("用户信息不能为空");
        }
        // 不为空，则验证用户名、密码、邮箱、手机号是否符合规范
        // 如果有一个不符合规则，则返回错误
        if ((!patternREGEX(USER_NAME_REGEX, user.getUsername()) ||
                (!patternREGEX(PASSWORD_REGEX, user.getPassword())) ||
                (!patternREGEX(EMAIL_REGEX, user.getEmail())) ||
                (!patternREGEX(PHONE_REGEX, user.getPhone()))
            )){
            return Result.error("用户信息格式错误");
        }
        // 检验 用户名、邮箱、手机号 是否唯一
        if (userMapper.getUserByUsername(user.getUsername()) != null ||
                userMapper.getUserByEmail(user.getEmail()) > 0 ||
                userMapper.getUserByPhone(user.getPhone()) > 0){
            return Result.error("用户已存在,手机号码或邮箱已有绑定用户");
        }
        // 密码加密
        String md5Password = DigestUtils.md5DigestAsHex(user.getPassword().getBytes());
        user.setPassword(md5Password);
        // 注册时间
        user.setGmtCreate(LocalDateTime.now());
        // 开始注册
        if (userMapper.insert(user) == 1){
            // openfeign调用接口，设置权限
            userFeignClient.bindDefaultRole(user.getUserId());
            return Result.success();
        }else {
            // 如果注册失败，则返回注册失败
            return Result.error("注册失败");
        }

    }


    /**
     * 分页查询
     *  权限校验：普通用户只返回自己
     *       管理员返回所有普通用户
     *       超级管理员返回所有用户
     * @param userPageDTO
     * @return
     */
    @Override
    @GlobalTransactional(rollbackFor = Exception.class)
    public Result<PageResult> pageQuery(UserPageDTO userPageDTO) {
        /*todo 根据权限校验结果返回：<br>普通用户仅查询自己，管理员能查所有普通用户，超管能查全部*/
        PageResult pageResult = new PageResult();
        List<User> users = new ArrayList<>();
        int start = (userPageDTO.getPage()-1) * userPageDTO.getPageSize();
        int pageSize = userPageDTO.getPageSize();

        // 1. 通过本地线程保存的用户ID，校验用户的权限
        Long localUserId = BaseContextUtil.getCurrentId();
        // 通过 openfeign 调用权限服务，查询用户权限
        Integer userRoleId = userFeignClient.getRoleId(localUserId);
        if(userRoleId == null)
            return Result.error("用户权限异常");

        // 2.如果是超级管理员，则返回所有用户
        if (userRoleId == 1){
            users = userMapper.getUsersList(start, pageSize);
        } else if (userRoleId == 3) {
            // 3.如果是管理员，则返回所有普通用户
            users = userMapper.getUsersListByInnerJoin(start, pageSize);
        } else if (userRoleId == 2) {
            // 4. 如果是普通用户，则返回自己 , 通过用户ID查询用户信息
            User user = userMapper.getUserByUserId(localUserId);
            user.setPassword(null);
            users.add(user);
        }else {
            return Result.error("用户权限异常");
        }
        pageResult.setTotal(users.size());
        pageResult.setRecords(users);
        return Result.success(pageResult);

    }

    /**
     * 更新用户基本信息
     * @param user
     * @return
     */
    @Override
    @GlobalTransactional(rollbackFor = Exception.class)
    public Result<User> updateUser(User user) {
        /*todo 根据权限限制：<br>普通用户改自己，管理员改普通用户，超管改所有*/

        // 先校验输入是否符合规范
        // userId 不能为空
        if (user.getUserId() == null) {
            return Result.error("用户ID不能为空");
        }
        // 如果有一个不符合规则，则返回错误
        if ((!patternREGEX(USER_NAME_REGEX, user.getUsername()) ||
                (!patternREGEX(PASSWORD_REGEX, user.getPassword())) ||
                (!patternREGEX(EMAIL_REGEX, user.getEmail())) ||
                (!patternREGEX(PHONE_REGEX, user.getPhone()))
        )){
            return Result.error("用户信息格式错误");
        }

        // 校验用户权限是否能够进行修改
        Long localUserId = BaseContextUtil.getCurrentId();
        // 调用权限服务
        if (!userFeignClient.permissionCheckout(localUserId, user.getUserId())) {
            return Result.error("权限不足");
        }

        // 检验 用户名、邮箱、手机号 是否唯一
        if (userMapper.getUserByUsername(user.getUsername()) != null ||
                userMapper.getUserByEmail(user.getEmail()) > 0 ||
                userMapper.getUserByPhone(user.getPhone()) > 0){
            return Result.error("用户已存在,手机号码或邮箱已有绑定用户");
        }

        // 对密码进行加密
        String md5Password = DigestUtils.md5DigestAsHex(user.getPassword().getBytes());
        user.setPassword(md5Password);

        // 更新用户信息
        userMapper.updateUser(user);

        return Result.success();
    }

    /**
     * 重置密码
     * @param userId
     * @return
     */
    @Override
    @GlobalTransactional(rollbackFor = Exception.class)
    public Result<User> resetPassword(Long userId) {
        // 1.通过本地线程保存的用户ID，校验用户的权限
        Long localUserId = BaseContextUtil.getCurrentId();

        // 2.通过 openfeign 调用权限服务，校验调用者是否有权限修改被调用者的基础信息
        Boolean permissionCheckout = userFeignClient.permissionCheckout(localUserId, userId);

        if (permissionCheckout){
            //  3.校验通过，重置密码
            String newPassword = DigestUtils.md5DigestAsHex(DEFAULT_PASSWORD.getBytes());
            userMapper.resetPassword(userId, newPassword);
            return Result.success("密码重置成功");
        }else {
            return Result.error("用户权限异常");
        }
    }

    /**
     * 修改用户权限 -- superAdmin专用
     * * todo 修改权限， 这个服务应该放在权限服务中，
     *      但是由于现在没写网关，若放在权限服务中，需要重新配置拦截器，
     *      暂时放在用户服务中，待日后迁移到权限服务中
     * @param userId
     * @param userNewRoleId
     * @return
     */
    @Override
    @GlobalTransactional(rollbackFor = Exception.class)
    public Result alterUserPermissionByUserId(Long userId, String userNewRoleId) {
        // 1.通过本地线程保存的用户ID，校验用户的权限
        Long localUserId = BaseContextUtil.getCurrentId();
        if (localUserId != 1)return Result.error("用户权限异常或操作异常");
        // 2.通过 openFeign 调用权限服务，校验调用者是否为超管，且操作是否合法
        Integer permissionCheckout = userFeignClient.getRoleId(userId);
        if (permissionCheckout != 2 )return Result.error("用户权限异常或操作异常");

        // 调用权限更新服务，更新用户权限
        Integer number = userFeignClient.alterUserPermission(userId, userNewRoleId);

        if(number == 0)
            return Result.error("修改权限失败");

        return Result.success("权限修改成功");
    }


    /**
     * 验证是否符合正则
     * 符合返回 1 ，不符合返回 0
     * @param regex
     * @param value
     * @return
     */
    public boolean patternREGEX(String regex, String value){
        Pattern pattern = Pattern.compile(regex);
        return pattern.matcher(value).matches();
    }

    /**
     * 构建jwt令牌
     *
     * @param user
     * @return
     */
    public Result<LoginUserVO> buildJwt(User user) {
        // 登录成功后，生成jwt令牌
        Map<String, Object> claims = new HashMap<>();
        claims.put("userId", user.getUserId());

        // 生成jwt令牌
        String token = JwtUtil.createJWT(jwtProperties.getUserSecretKey(), jwtProperties.getUserTtl(),claims);
        LoginUserVO loginUserVO = LoginUserVO.builder()
                .userId(user.getUserId())
                .username(user.getUsername())
                .email(user.getEmail())
                .phone(user.getPhone())
                .token(token)
                .build();

        return Result.success(loginUserVO);
    }
}
